from fastapi import APIRouter, Body, Path, Query, Depends
from backend.db.neo4j_driver import neo4j_driver
from backend.utils.logger import logger
from backend.utils.response_filter import filter_sensitive_fields
from backend.schemas.workbench import WorkbenchCreate, WorkbenchUpdate
from backend.auth.auth import get_current_user
from datetime import datetime
from typing import Optional
from backend.schemas.workbench_process_step import WorkBenchProcessStepCreate, WorkBenchProcessStepUpdate, WorkBenchProcessStepDetail
from backend.schemas.workbench_process import WorkBenchProcessToListSchema
from backend.utils.consts import SERVER_INTERNAL_ERROR_MSG, WORKBENCH_NOT_FOUND_MSG, PROCESS_NOT_FOUND_MSG, TRANSPORT_NOT_FOUND_MSG, PALLET_TYPE_NOT_FOUND_MSG, LINE_NOT_FOUND_MSG, BINDING_NOT_FOUND_MSG, BLACKLIST_NOT_FOUND_MSG, ALREADY_BOUND_MSG, ALREADY_IN_BLACKLIST_MSG, ALREADY_IN_WHITELIST_MSG, MISSING_EQUIPMENT_ID_MSG, MISSING_PROCESS_ID_MSG, MISSING_STEP_ID_OR_TIME_MSG, MISSING_TRANSPORT_ID_MSG, WORKBENCH_TYPE_NOT_FOUND_MSG, TOOL_PROOF_NOT_FOUND_MSG
from backend.cypher_defs.cypher_workbench import (
    MATCH_WORKBENCH_BY_ID,
    MATCH_WORKBENCH_TYPE_BY_ID,
    MATCH_PROCESS_BY_ID,
    MATCH_TOOL_PROOF_BY_ID,
    GET_WORKBENCH_LIST,
    CREATE_WORKBENCH,
    UPDATE_WORKBENCH_TYPE,
    UPDATE_WORKBENCH,
    DELETE_WORKBENCH,
    GET_WORKBENCH_EQUIPMENTS,
    CHECK_EQUIPMENT_EXISTS,
    CHECK_EQUIPMENT_BOUND,
    DELETE_SINGLE_TYPE_EQUIPMENT,
    ADD_WORKBENCH_EQUIPMENT,
    MATCH_TRANSPORT_BY_ID,
    MATCH_WORKBENCH_TRANSPORT_REL,
    CHECK_WORKBENCH_TRANSPORT_BOUND,
    CREATE_WORKBENCH_TRANSPORT_REL,
    DELETE_WORKBENCH_TRANSPORT_REL,
    MATCH_PROCESS_STEP_BY_ID,
    MATCH_WORKBENCH_PROCESS_STEP_REL,
    CHECK_WORKBENCH_PROCESS_STEP_BOUND,
    CREATE_WORKBENCH_PROCESS_STEP_REL,
    UPDATE_WORKBENCH_PROCESS_STEP_REL,
    DELETE_WORKBENCH_PROCESS_STEP_REL,
    MATCH_WORKBENCH_TYPE_TOOL_PROOF_REL,
    CREATE_WORKBENCH_TYPE_TOOL_PROOF_REL,
    DELETE_WORKBENCH_TYPE_TOOL_PROOF_REL
)

router = APIRouter(prefix="/api/workbenches", tags=["workbench"])

@router.get(
    "/",
    response_model=dict,
    summary="获取工台列表",
    description="支持按工台名称或编号模糊查询，返回工台及其类型信息"
)
async def get_workbench_list(
    keyword: str = Query(None, description="工台名称或工台编号")
):
    try:
        logger.info("Request: GET /api/workbenches")
        cypher = GET_WORKBENCH_LIST.format(where=" WHERE w.benchName CONTAINS $keyword OR w.benchId CONTAINS $keyword" if keyword else "")
        params = {}
        if keyword:
            params["keyword"] = keyword
        result = neo4j_driver.query(cypher, params)
        data = []
        for r in result:
            w = r["w"]
            w["benchTypeName"] = r.get("benchTypeName")
            data.append(w)
        # data = filter_sensitive_fields(data)
        logger.info("Workbench list returned.")
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_list: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/",
    response_model=dict,
    summary="创建工台",
    description="创建新的工台节点，需指定工台类型。工台ID不可重复。"
)
async def create_workbench(
    body: dict = Body(...),  # 用dict接收，benchTypeId为必填
    current_user: str = Depends(get_current_user)
):
    try:
        cypher = CREATE_WORKBENCH
        if neo4j_driver.query(cypher, {"benchId": body["benchId"]}):
            return {"code": 1011, "msg": "工台ID已存在", "data": None}
        # 检查工台类型是否存在
        cypher = MATCH_WORKBENCH_TYPE_BY_ID
        if not neo4j_driver.query(cypher, {"benchTypeId": body["benchTypeId"]}):
            return {"code": 1014, "msg": WORKBENCH_TYPE_NOT_FOUND_MSG, "data": None}
        now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        params = {k: v for k, v in body.items() if k != "benchTypeId"}
        params["createdBy"] = current_user
        params["createdAt"] = now_str
        params["updatedBy"] = current_user
        params["updatedAt"] = now_str
        params["benchId"] = body["benchId"]
        cypher = CREATE_WORKBENCH
        params["benchTypeId"] = body["benchTypeId"]
        result = neo4j_driver.query(cypher, params)
        data = result[0]["w"] if result else None
        if data:
            data["benchTypeName"] = result[0].get("benchTypeName")
        data = filter_sensitive_fields(data)
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in create_workbench: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.put(
    "/{benchId}",
    response_model=dict,
    summary="更新工台信息",
    description="根据工台ID更新工台基本信息及类型。"
)
async def update_workbench(
    benchId: str = Path(...),
    body: dict = Body(...),  # 用dict接收，benchTypeId为可选
    current_user: str = Depends(get_current_user)
):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        if not result:
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        params = {"benchId": benchId}
        set_clauses = []
        for k, v in body.items():
            if k in ["benchName", "status", "cycleTime"]:
                set_clauses.append(f"w.{k} = ${k}")
                params[k] = v
        now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        set_clauses.append("w.updatedBy = $updatedBy")
        set_clauses.append("w.updatedAt = $updatedAt")
        params["updatedBy"] = current_user
        params["updatedAt"] = now_str
        if "benchTypeId" in body:
            # 检查新类型是否存在
            cypher = MATCH_WORKBENCH_TYPE_BY_ID
            if not neo4j_driver.query(cypher, {"benchTypeId": body["benchTypeId"]}):
                return {"code": 1014, "msg": WORKBENCH_TYPE_NOT_FOUND_MSG, "data": None}
            cypher = UPDATE_WORKBENCH_TYPE.replace('{set_clauses}', ', '.join(set_clauses))
            params["benchTypeId"] = body["benchTypeId"]
        else:
            cypher = UPDATE_WORKBENCH.replace('{set_clauses}', ', '.join(set_clauses))
        result = neo4j_driver.query(cypher, params)
        data = result[0]["w"] if result else None
        if data:
            data["benchTypeName"] = result[0].get("benchTypeName")
        data = filter_sensitive_fields(data)
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in update_workbench: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.delete(
    "/{benchId}",
    response_model=dict,
    summary="删除工台",
    description="根据工台ID删除工台节点及其所有关联关系。"
)
async def delete_workbench(benchId: str = Path(...)):
    try:
        # 1. benchId存在
        cypher = DELETE_WORKBENCH
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        if not result:
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        # 2. 删除节点及关系
        cypher = DELETE_WORKBENCH
        neo4j_driver.query(cypher, {"benchId": benchId})
        return {"code": 0, "msg": "success", "data": None}
    except Exception as e:
        logger.error(f"Error in delete_workbench: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/equipments",
    response_model=dict,
    summary="获取工台关联设备",
    description="查询指定工台已绑定的所有设备信息，包含设备类型名称、是否必须、是否单一。"
)
async def get_workbench_equipments(benchId: str = Path(...)):
    try:
        cypher = GET_WORKBENCH_EQUIPMENTS
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        data = []
        for r in result:
            e = r["e"]
            e["equipmentTypeName"] = r.get("equipmentTypeName")
            e["isMust"] = True if str(r.get("isMust")).lower() == 'true' else False
            e["isSingle"] = True if str(r.get("isSingle")).lower() == 'true' else False
            data.append(e)
        data = filter_sensitive_fields(data)
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_equipments: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/{benchId}/equipments",
    response_model=dict,
    summary="添加工台关联设备",
    description="为指定工台绑定设备，若设备类型isSingle为true则替换原有同类型设备。"
)
async def add_workbench_equipment(benchId: str = Path(...), body: dict = Body(...)):
    try:
        equipmentId = body.get("equipmentId")
        if not equipmentId:
            return {"code": 400, "msg": "缺少equipmentId", "data": None}
        # 校验工台是否存在
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        # 校验设备是否存在
        cypher = CHECK_EQUIPMENT_EXISTS
        result = neo4j_driver.query(cypher, {"equipmentId": equipmentId})
        if not result:
            return {"code": 1014, "msg": "设备不存在", "data": None}
        equipmentTypeId = result[0]["equipmentTypeId"]
        isSingle = str(result[0].get("isSingle")).lower() == 'true'
        # 检查是否已存在关系
        cypher = CHECK_EQUIPMENT_BOUND
        if neo4j_driver.query(cypher, {"benchId": benchId, "equipmentId": equipmentId}):
            return {"code": 1015, "msg": "该设备已关联", "data": None}
        # 若isSingle为true，先删除工台下该类型设备
        if isSingle:
            cypher = DELETE_SINGLE_TYPE_EQUIPMENT
            neo4j_driver.query(cypher, {"benchId": benchId, "equipmentTypeId": equipmentTypeId})
        # 建立关系
        cypher = ADD_WORKBENCH_EQUIPMENT
        neo4j_driver.query(cypher, {"benchId": benchId, "equipmentId": equipmentId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_equipment: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/blacklist",
    response_model=dict,
    summary="获取工台黑名单工序",
    description="查询指定工台的工序黑名单列表。"
)
async def get_workbench_blacklist(benchId: str = Path(...)):
    try:
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_BLACK_LIST]->(p:WorkBenchProcess) RETURN p"
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        data = [r["p"] for r in result]
        data = filter_sensitive_fields(data)
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_blacklist: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/{benchId}/blacklist",
    response_model=dict,
    summary="添加工序到工台黑名单",
    description="为指定工台添加工序黑名单，需校验工台和工序均存在且未重复添加，且与白名单互斥。"
)
async def add_workbench_blacklist(benchId: str = Path(...), body: WorkBenchProcessToListSchema = Body(...)):
    try:
        processId = body.processId
        if not processId:
            return {"code": 400, "msg": "缺少processId", "data": None}
        # 校验工台是否存在
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        # 校验工序是否存在
        cypher = MATCH_PROCESS_BY_ID
        if not neo4j_driver.query(cypher, {"processId": processId}):
            return {"code": 1014, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        # 检查是否已存在黑名单关系
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_BLACK_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN p"
        if neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
            return {"code": 1015, "msg": "该工序已在黑名单", "data": None}
        # 检查是否已存在白名单关系（互斥）
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_WHITE_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN p"
        if neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
            return {"code": 1017, "msg": "该工序已在白名单，禁止添加到黑名单", "data": None}
        # 建立关系
        cypher = "MATCH (b:WorkBench {benchId: $benchId}), (p:WorkBenchProcess {processId: $processId}) CREATE (b)-[:WORKBENCH_BLACK_LIST]->(p)"
        neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_blacklist: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/whitelist",
    response_model=dict,
    summary="获取工台白名单工序",
    description="查询指定工台的工序白名单列表。"
)
async def get_workbench_whitelist(benchId: str = Path(...)):
    try:
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_WHITE_LIST]->(p:WorkBenchProcess) RETURN p"
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        data = [r["p"] for r in result]
        data = filter_sensitive_fields(data)
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_whitelist: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/{benchId}/whitelist",
    response_model=dict,
    summary="添加工序到工台白名单",
    description="为指定工台添加工序白名单，需校验工台和工序均存在且未重复添加，且与黑名单互斥。"
)
async def add_workbench_whitelist(benchId: str = Path(...), body: WorkBenchProcessToListSchema = Body(...)):
    try:
        processId = body.processId
        if not processId:
            return {"code": 400, "msg": "缺少processId", "data": None}
        # 校验工台是否存在
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        # 校验工序是否存在
        cypher = MATCH_PROCESS_BY_ID
        if not neo4j_driver.query(cypher, {"processId": processId}):
            return {"code": 1014, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        # 检查是否已存在白名单关系
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_WHITE_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN p"
        if neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
            return {"code": 1015, "msg": "该工序已在白名单", "data": None}
        # 检查是否已存在黑名单关系（互斥）
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_BLACK_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN p"
        if neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
            return {"code": 1016, "msg": "该工序已在黑名单，禁止添加到白名单", "data": None}
        # 建立关系
        cypher = "MATCH (b:WorkBench {benchId: $benchId}), (p:WorkBenchProcess {processId: $processId}) CREATE (b)-[:WORKBENCH_WHITE_LIST]->(p)"
        neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_whitelist: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/transports",
    response_model=dict,
    summary="获取工台关联传送方式",
    description="查询指定工台已绑定的所有传送方式。"
)
async def get_workbench_transports(benchId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_TRANSPORT]->(m:MaterialTransport) RETURN m"
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        data = [r["m"] for r in result]
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_transports: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/{benchId}/transports",
    response_model=dict,
    summary="工台绑定传送方式",
    description="为指定工台绑定传送方式，需校验工台和传送方式均存在且未重复绑定。"
)
async def add_workbench_transport(benchId: str = Path(...), body: dict = Body(...)):
    try:
        materialTransportId = body.get("materialTransportId")
        if not materialTransportId:
            return {"code": 400, "msg": "缺少materialTransportId", "data": None}
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_TRANSPORT_BY_ID
        if not neo4j_driver.query(cypher, {"materialTransportId": materialTransportId}):
            return {"code": 1042, "msg": "传送方式不存在", "data": None}
        cypher = CHECK_WORKBENCH_TRANSPORT_BOUND
        if neo4j_driver.query(cypher, {"benchId": benchId, "materialTransportId": materialTransportId}):
            return {"code": 1043, "msg": "存在工台绑定关系，禁止重复绑定", "data": None}
        cypher = CREATE_WORKBENCH_TRANSPORT_REL
        neo4j_driver.query(cypher, {"benchId": benchId, "materialTransportId": materialTransportId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_transport: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.delete(
    "/{benchId}/transports/{transportId}",
    response_model=dict,
    summary="工台解绑传送方式",
    description="解除工台与传送方式的绑定关系，需校验关系存在。"
)
async def remove_workbench_transport(benchId: str = Path(...), transportId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_TRANSPORT_BY_ID
        if not neo4j_driver.query(cypher, {"transportId": transportId}):
            return {"code": 1042, "msg": "传送方式不存在", "data": None}
        cypher = MATCH_WORKBENCH_TRANSPORT_REL
        if not neo4j_driver.query(cypher, {"benchId": benchId, "materialTransportId": transportId}):
            return {"code": 1061, "msg": "传送方式未关联工台", "data": None}
        cypher = DELETE_WORKBENCH_TRANSPORT_REL
        neo4j_driver.query(cypher, {"benchId": benchId, "materialTransportId": transportId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in remove_workbench_transport: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/process-steps",
    response_model=dict,
    summary="获取工台工序列表",
    description="查询指定工台已绑定的所有工序及其耗时信息。"
)
async def get_workbench_process_steps(benchId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_HAS_OPERATION]->(s:WorkBenchProcessStep) RETURN s"
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        data = [r["s"] for r in result]
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_process_steps: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/process-steps/{stepId}",
    response_model=dict,
    summary="获取工台工序详情",
    description="查询指定工台下某工序的详细信息。"
)
async def get_workbench_process_step_detail(benchId: str = Path(...), stepId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_PROCESS_STEP_BY_ID
        if not neo4j_driver.query(cypher, {"stepId": stepId}):
            return {"code": 1023, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_WORKBENCH_PROCESS_STEP_REL
        result = neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId})
        if not result:
            return {"code": 1043, "msg": BINDING_NOT_FOUND_MSG, "data": None}
        return {"code": 0, "msg": "success", "data": result[0]["s"]}
    except Exception as e:
        logger.error(f"Error in get_workbench_process_step_detail: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/{benchId}/process-steps",
    response_model=dict,
    summary="新建工台工序关联",
    description="为指定工台绑定工序及耗时，需校验工台和工序均存在且未重复绑定。"
)
async def add_workbench_process_step(benchId: str = Path(...), body: WorkBenchProcessStepCreate = Body(...)):
    try:
        stepId = body.stepId
        actualTime = body.actualTime
        if not stepId or actualTime is None:
            return {"code": 400, "msg": "缺少stepId或actualTime", "data": None}
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_PROCESS_STEP_BY_ID
        if not neo4j_driver.query(cypher, {"stepId": stepId}):
            return {"code": 1023, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        cypher = CHECK_WORKBENCH_PROCESS_STEP_BOUND
        if neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId}):
            return {"code": 1043, "msg": BINDING_NOT_FOUND_MSG, "data": None}
        cypher = CREATE_WORKBENCH_PROCESS_STEP_REL
        neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId, "actualTime": actualTime})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_process_step: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.put(
    "/{benchId}/process-steps/{stepId}",
    response_model=dict,
    summary="更新工台工序耗时",
    description="更新指定工台下某工序的实际耗时。"
)
async def update_workbench_process_step(benchId: str = Path(...), stepId: str = Path(...), body: WorkBenchProcessStepUpdate = Body(...)):
    try:
        actualTime = body.actualTime
        if actualTime is None:
            return {"code": 400, "msg": "缺少actualTime", "data": None}
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_PROCESS_STEP_BY_ID
        if not neo4j_driver.query(cypher, {"stepId": stepId}):
            return {"code": 1023, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        cypher = CHECK_WORKBENCH_PROCESS_STEP_BOUND
        if not neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId}):
            return {"code": 1043, "msg": BINDING_NOT_FOUND_MSG, "data": None}
        cypher = UPDATE_WORKBENCH_PROCESS_STEP_REL
        neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId, "actualTime": actualTime})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in update_workbench_process_step: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.delete(
    "/{benchId}/process-steps/{stepId}",
    response_model=dict,
    summary="删除工台工序关联",
    description="解除工台与工序的绑定关系，需校验关系存在。"
)
async def delete_workbench_process_step(benchId: str = Path(...), stepId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_PROCESS_STEP_BY_ID
        if not neo4j_driver.query(cypher, {"stepId": stepId}):
            return {"code": 1023, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
        cypher = CHECK_WORKBENCH_PROCESS_STEP_BOUND
        if not neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId}):
            return {"code": 1043, "msg": BINDING_NOT_FOUND_MSG, "data": None}
        cypher = DELETE_WORKBENCH_PROCESS_STEP_REL
        neo4j_driver.query(cypher, {"benchId": benchId, "stepId": stepId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in delete_workbench_process_step: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.post(
    "/workbench-types/{benchTypeId}/tool-proofs",
    response_model=dict,
    summary="工台类型绑定防呆措施",
    description="为工台类型绑定防呆措施，需校验类型和措施均存在且未重复绑定。"
)
async def add_workbench_type_tool_proof(benchTypeId: str = Path(...), body: dict = Body(...)):
    try:
        fpyeId = body.get("fpyeId")
        if not fpyeId:
            return {"code": 400, "msg": "缺少fpyeId", "data": None}
        # 校验工台类型是否存在
        cypher = MATCH_WORKBENCH_TYPE_BY_ID
        if not neo4j_driver.query(cypher, {"benchTypeId": benchTypeId}):
            return {"code": 1014, "msg": WORKBENCH_TYPE_NOT_FOUND_MSG, "data": None}
        # 校验防呆措施是否存在
        cypher = MATCH_TOOL_PROOF_BY_ID
        if not neo4j_driver.query(cypher, {"fpyeId": fpyeId}):
            return {"code": 1051, "msg": TOOL_PROOF_NOT_FOUND_MSG, "data": None}
        # 检查是否已存在关系
        cypher = MATCH_WORKBENCH_TYPE_TOOL_PROOF_REL
        if neo4j_driver.query(cypher, {"benchTypeId": benchTypeId, "fpyeId": fpyeId}):
            return {"code": 1052, "msg": "已存在绑定关系，禁止重复绑定", "data": None}
        # 建立关系
        cypher = CREATE_WORKBENCH_TYPE_TOOL_PROOF_REL
        neo4j_driver.query(cypher, {"benchTypeId": benchTypeId, "fpyeId": fpyeId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in add_workbench_type_tool_proof: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.delete(
    "/workbench-types/{benchTypeId}/tool-proofs/{fpyeId}",
    response_model=dict,
    summary="工台类型解绑防呆措施",
    description="解除工台类型与防呆措施的绑定关系，需校验关系存在。"
)
async def remove_workbench_type_tool_proof(benchTypeId: str = Path(...), fpyeId: str = Path(...)):
    try:
        cypher = MATCH_WORKBENCH_TYPE_BY_ID
        if not neo4j_driver.query(cypher, {"benchTypeId": benchTypeId}):
            return {"code": 1014, "msg": WORKBENCH_TYPE_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_TOOL_PROOF_BY_ID
        if not neo4j_driver.query(cypher, {"fpyeId": fpyeId}):
            return {"code": 1051, "msg": TOOL_PROOF_NOT_FOUND_MSG, "data": None}
        cypher = MATCH_WORKBENCH_TYPE_TOOL_PROOF_REL
        if not neo4j_driver.query(cypher, {"benchTypeId": benchTypeId, "fpyeId": fpyeId}):
            return {"code": 1053, "msg": "不存在绑定关系", "data": None}
        cypher = DELETE_WORKBENCH_TYPE_TOOL_PROOF_REL
        neo4j_driver.query(cypher, {"benchTypeId": benchTypeId, "fpyeId": fpyeId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in remove_workbench_type_tool_proof: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.put(
    "/{benchId}/selected-tool-proof",
    response_model=dict,
    summary="工台选择防呆措施",
    description="为指定工台选择防呆措施，自动替换原有选择。"
)
async def select_workbench_tool_proof(benchId: str = Path(...), body: dict = Body(...)):
    try:
        fpyeId = body.get("fpyeId")
        if not fpyeId:
            return {"code": 400, "msg": "缺少fpyeId", "data": None}
        cypher = MATCH_WORKBENCH_BY_ID
        if not neo4j_driver.query(cypher, {"benchId": benchId}):
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        cypher = "MATCH (f:ToolProof {fpyeId: $fpyeId}) RETURN f"
        if not neo4j_driver.query(cypher, {"fpyeId": fpyeId}):
            return {"code": 1051, "msg": TOOL_PROOF_NOT_FOUND_MSG, "data": None}
        # 先删除原有选择
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[r:WORKBENCH_SELECTED_TOOL_PROOF]->(:ToolProof) DELETE r"
        neo4j_driver.query(cypher, {"benchId": benchId})
        # 建立新选择
        cypher = "MATCH (b:WorkBench {benchId: $benchId}), (f:ToolProof {fpyeId: $fpyeId}) CREATE (b)-[:WORKBENCH_SELECTED_TOOL_PROOF]->(f)"
        neo4j_driver.query(cypher, {"benchId": benchId, "fpyeId": fpyeId})
        return {"code": 0, "msg": "success"}
    except Exception as e:
        logger.error(f"Error in select_workbench_tool_proof: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/{benchId}/available-tool-proofs",
    response_model=dict,
    summary="获取工台可选防呆措施",
    description="查询工台类型下所有可选的防呆措施。"
)
async def get_workbench_available_tool_proofs(benchId: str = Path(...)):
    try:
        cypher = "MATCH (b:WorkBench {benchId: $benchId})-[:WORKBENCH_IS_TYPE_OF]->(t:WorkBenchType) RETURN t.benchTypeId AS benchTypeId"
        result = neo4j_driver.query(cypher, {"benchId": benchId})
        if not result:
            return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
        benchTypeId = result[0]["benchTypeId"]
        cypher = "MATCH (t:WorkBenchType {benchTypeId: $benchTypeId})-[:WORKBENCH_TYPE_HAS_TOOL_PROOF]->(f:ToolProof) RETURN f"
        result = neo4j_driver.query(cypher, {"benchTypeId": benchTypeId})
        data = [r["f"] for r in result]
        return {"code": 0, "msg": "success", "data": data}
    except Exception as e:
        logger.error(f"Error in get_workbench_available_tool_proofs: {e}", exc_info=True)
        return {"code": 500, "msg": SERVER_INTERNAL_ERROR_MSG, "data": None}

@router.get(
    "/processes",
    response_model=dict,
    summary="获取工序列表",
    description="支持按工序名称或编号模糊查询，返回所有工序信息。"
)
async def get_process_list(keyword: Optional[str] = Query(None, description="工序名称或编号")):
    pass  # ... existing code ...

@router.post(
    "/processes",
    response_model=dict,
    summary="创建工序",
    description="创建新的工序节点，工序ID不可重复。"
)
async def create_process(body: dict = Body(...), current_user: str = Depends(get_current_user)):
    pass  # ... existing code ...

@router.put(
    "/processes/{processId}",
    response_model=dict,
    summary="更新工序信息",
    description="根据工序ID更新工序基本信息。"
)
async def update_process(processId: str = Path(...), body: dict = Body(...), current_user: str = Depends(get_current_user)):
    pass  # ... existing code ...

@router.delete(
    "/processes/{processId}",
    response_model=dict,
    summary="删除工序",
    description="根据工序ID删除工序节点及其所有关联关系。"
)
async def delete_process(processId: str = Path(...)):
    pass  # ... existing code ...

@router.get(
    "/processes/{processId}",
    response_model=dict,
    summary="获取工序详情",
    description="根据工序ID获取工序的详细信息。"
)
async def get_process_detail(processId: str = Path(...)):
    pass  # ... existing code ...

@router.delete(
    "/{benchId}/blacklist",
    response_model=dict,
    summary="删除工台黑名单工序",
    description="将指定工序从工台黑名单移除，需校验关系存在。"
)
async def remove_workbench_blacklist(
    benchId: str = Path(...),
    body: WorkBenchProcessToListSchema = Body(...)
):
    processId = body.processId
    cypher = MATCH_WORKBENCH_BY_ID
    if not neo4j_driver.query(cypher, {"benchId": benchId}):
        return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
    cypher = MATCH_PROCESS_BY_ID
    if not neo4j_driver.query(cypher, {"processId": processId}):
        return {"code": 1014, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
    cypher = "MATCH (b:WorkBench {benchId: $benchId})-[r:WORKBENCH_BLACK_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN r"
    if not neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
        return {"code": 1061, "msg": "黑名单关系不存在", "data": None}
    cypher = "MATCH (b:WorkBench {benchId: $benchId})-[r:WORKBENCH_BLACK_LIST]->(p:WorkBenchProcess {processId: $processId}) DELETE r"
    neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId})
    return {"code": 0, "msg": "success"}

@router.delete(
    "/{benchId}/whitelist",
    response_model=dict,
    summary="删除工台白名单工序",
    description="将指定工序从工台白名单移除，需校验关系存在。"
)
async def remove_workbench_whitelist(
    benchId: str = Path(...),
    body: WorkBenchProcessToListSchema = Body(...)
):
    processId = body.processId
    cypher = MATCH_WORKBENCH_BY_ID
    if not neo4j_driver.query(cypher, {"benchId": benchId}):
        return {"code": 1013, "msg": WORKBENCH_NOT_FOUND_MSG, "data": None}
    cypher = MATCH_PROCESS_BY_ID
    if not neo4j_driver.query(cypher, {"processId": processId}):
        return {"code": 1014, "msg": PROCESS_NOT_FOUND_MSG, "data": None}
    cypher = "MATCH (b:WorkBench {benchId: $benchId})-[r:WORKBENCH_WHITE_LIST]->(p:WorkBenchProcess {processId: $processId}) RETURN r"
    if not neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId}):
        return {"code": 1061, "msg": "白名单关系不存在", "data": None}
    cypher = "MATCH (b:WorkBench {benchId: $benchId})-[r:WORKBENCH_WHITE_LIST]->(p:WorkBenchProcess {processId: $processId}) DELETE r"
    neo4j_driver.query(cypher, {"benchId": benchId, "processId": processId})
    return {"code": 0, "msg": "success"} 